#include <iostream>
#include <sys/epoll.h>
#include <memory>
#include "Log.hpp"
#include "Socket.hpp"

using namespace log_ns;
using namespace socket_ns;

class EpollServer
{
    const static int size = 128;
    const static int num = 128;

public:
    EpollServer(uint16_t port) : _port(port), _listensockfd(std::make_unique<TcpSocket>())
    {
        _listensockfd->BuildListenSocket(_port);
        _epfd = ::epoll_create(size);
        if (_epfd < 0)
        {
            // 创建失败
            LOG(FATAL, "epoll create error!\n");
            exit(1);
        }
        LOG(INFO, "epoll create sucess, epfd: %d\n", _epfd);
    }

    void Init()
    {
        // 将listen套接字添加到epoll中
        struct epoll_event event;
        event.data.fd = _listensockfd->Sockfd();
        event.events = EPOLLIN;
        int n = ::epoll_ctl(_epfd, EPOLL_CTL_ADD, _listensockfd->Sockfd(), &event);
        if (n < 0)
        {
            LOG(FATAL, "epoll ctl error!\n");
            exit(1);
        }
        LOG(FATAL, "epoll ctl sucess, add new fd: %d!\n", _listensockfd->Sockfd());
    }

    std::string EventToString(uint32_t events)
    {
        std::string eventstr;
        if (events & EPOLLIN)
        {
            eventstr = "EPOLLIN";
        }
        if (events & EPOLLOUT)
        {
            eventstr += "|EPOLLIN";
        }
        return eventstr;
    }
    void Accepter()
    {
        // 新连接到来
        InetAddr addr;
        int sockfd = _listensockfd->Accepter(&addr);
        if (sockfd < 0)
        {
            LOG(ERROR, "获取连接失败\n");
            return;
        }
        LOG(INFO, "得到一个新连接: %d, 客户端信息: %s\n", sockfd, addr.AddrStr());
        struct epoll_event event;
        event.data.fd = sockfd;
        event.events = EPOLLIN;
        epoll_ctl(_epfd, EPOLL_CTL_ADD, sockfd, &event);
    }

    void HandlerIO(int fd)
    {
        char buffer[1024];
        int n = ::recv(fd, buffer, sizeof(buffer) - 1, 0);
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << buffer;
            std::string response = "HTTP/1.0 200 OK\r\n";
            std::string content = "<html><body><h1>hello bite, hello world</h1></body></html>";
            response += "Content-Type: text/html\r\n";
            response += "Content-Length: " + std::to_string(content.size()) + "\r\n";
            response += "\r\n";
            response += content;

            ::send(fd, response.c_str(), response.size(), 0);
        }
        else if (n == 0)
        {
            LOG(INFO, "client quit, close fd: %d\n", fd);
            // epoll移除的必须是合法fd
            epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            // 关闭fd
            ::close(fd);
        }
        else
        {
            LOG(INFO, "recv error, close fd: %d\n", fd);
            epoll_ctl(_epfd, EPOLL_CTL_DEL, fd, nullptr);
            // 关闭fd
            ::close(fd);
        }
    }
    void HandlerEvent(int n)
    {
        for (int i = 0; i < n; ++i)
        {
            int fd = revs[i].data.fd;
            uint32_t revents = revs[i].events;

            LOG(INFO, "%d 上面有事件就绪了, 具体事件：%s\n", fd, EventToString(revents).c_str());
            if (revents & EPOLLIN)
            {
                if (fd == _listensockfd->Sockfd())
                {
                    Accepter();
                }
                else
                {
                    HandlerIO(fd);
                }
            }
        }
    }

    void Loop()
    {
        int timeout = 1000;
        while (true)
        {
            // 阻塞：_listensockfd->Accepter();

            int n = epoll_wait(_epfd, revs, num, timeout);
            if (n < 0)
            {
                LOG(INFO, "epoll wait error...\n");
            }
            else if (n == 0)
            {
                LOG(INFO, "epoll wait time out...\n");
            }
            else
            {
                LOG(INFO, "have event happend! n: %d\n", n);
                HandlerEvent(n);
            }
        }
    }

    ~EpollServer()
    {
    }

private:
    int _epfd;
    uint16_t _port;
    std::unique_ptr<Socket> _listensockfd;
    struct epoll_event revs[num];
};